/*
* Licensee agrees that the example code provided to Licensee has been developed and released by Bosch solely as an example to be used as a potential reference for application development by Licensee. 
* Fitness and suitability of the example code for any use within application developed by Licensee need to be verified by Licensee on its own authority by taking appropriate state of the art actions and measures (e.g. by means of quality assurance measures).
* Licensee shall be responsible for conducting the development of its applications as well as integration of parts of the example code into such applications, taking into account the state of the art of technology and any statutory regulations and provisions applicable for such applications. Compliance with the functional system requirements and testing there of (including validation of information/data security aspects and functional safety) and release shall be solely incumbent upon Licensee. 
* For the avoidance of doubt, Licensee shall be responsible and fully liable for the applications and any distribution of such applications into the market.
* 
* 
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions are 
* met:
* 
*     (1) Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer. 
* 
*     (2) Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in
*     the documentation and/or other materials provided with the
*     distribution.  
*     
*     (3)The name of the author may not be used to
*     endorse or promote products derived from this software without
*     specific prior written permission.
* 
*  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
*  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
*  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
*  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
*  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
*  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
*  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
*  POSSIBILITY OF SUCH DAMAGE.
*/
/*----------------------------------------------------------------------------*/

/* module includes ********************************************************** */

#include "XdkAppInfo.h"
/* own header files */
#undef BCDS_MODULE_ID
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_TXT_PARSER

/* own header files */
#include "AppTxtParser.h"

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "AppController.h"
#include "BCDS_Assert.h"

/* constant definitions ***************************************************** */

#define APP_TXT_PARSER_EMPTY                       ""
#define APP_TXT_PARSER_YES                         "YES"
#define APP_TXT_PARSER_NO                          "NO"

#if WLAN_STATIC_IP_DEFAULT
#define WLAN_STATIC_IP_TXT_DEFAULT              APP_TXT_PARSER_YES        /**< Network static IP Address Default*/
#else /* WLAN_STATIC_IP_DEFAULT */
#define WLAN_STATIC_IP_TXT_DEFAULT              APP_TXT_PARSER_NO        /**< Network static IP Address Default*/
#endif /* WLAN_STATIC_IP_DEFAULT */

#if LWM2M_CON_NOTIFIES_DEFAULT
#define LWM2M_CON_NOTIFIES_TXT_DEFAULT      APP_TXT_PARSER_YES        /**< LWM2M binding by Default*/
#else /* LWM2M_CON_NOTIFIES_DEFAULT */
#define LWM2M_CON_NOTIFIES_TXT_DEFAULT      APP_TXT_PARSER_NO        /**< LWM2M binding by Default*/
#endif /* LWM2M_CON_NOTIFIES_DEFAULT */

#define APP_TXT_PARSER_LINE_SIZE_MAX               UINT8_C(65)

#define APP_TXT_PARSER_WHITESPACE                  "\t\n\r "
#define APP_TXT_PARSER_SPACE                       "\t "

#define LWM2M_TESTMODE_OFF                 UINT8_C(0)
#define LWM2M_TESTMODE_ON                  UINT8_C(1)
#define LWM2M_TESTMODE_MIX                 UINT8_C(2)

/* local variables ********************************************************** */

/** Variable containers for configuration values */

/** Attribute names on the configuration file */
static const char A1Name[] = "SSID";
static const char A2Name[] = "PASSWORD";
static const char A3Name[] = "STATIC";
static const char A4Name[] = "STATICIP";
static const char A5Name[] = "DNSSERVER";
static const char A6Name[] = "GATEWAY";
static const char A7Name[] = "MASK";
static const char A8Name[] = "LWM2MDEFSRV";
static const char A9Name[] = "LWM2MENDPOINT";
static const char A10Name[] = "DTLSPSKIDENTITY";
static const char A11Name[] = "DTLSPSKSECRETKEY";
static const char A12Name[] = "DTLSPSKSECRETKEYHEX";
static const char A13Name[] = "TESTMODE";
static const char A14Name[] = "LWM2MLIFETIME";
static const char A15Name[] = "LWM2MBINDING";
static const char A16Name[] = "LWM2MCONNOTIFIES";
static const char A17Name[] = "LWM2MDTLSSUFFIX";
static const char A18Name[] = "ACTIVATEDTLSPSK";

/**
 * INDEX at the configuration array
 */
enum AppTxtParser_ConfigArrayIndex_E
{
    ATP_IDX_WLAN_SSID,
    ATP_IDX_WLAN_PASSWORD,
    ATP_IDX_WLAN_STATIC_IP,
    ATP_IDX_WLAN_IP_ADDR,
    ATP_IDX_WLAN_DNS_ADDR,
    ATP_IDX_WLAN_GW_ADDR,
    ATP_IDX_WLAN_MASK,
    ATP_IDX_LWM2M_DEFAULT_SERVER,
    ATP_IDX_LWM2M_ENDPOINT,
    ATP_IDX_DTLS_PSK_IDENTITY,
    ATP_IDX_DTLS_PSK_SECRET_KEY,
    ATP_IDX_DTLS_PSK_SECRET_KEY_HEX,
    ATP_IDX_TEST_MODE,
    ATP_IDX_LWM2M_LIFETIME,
    ATP_IDX_LWM2M_BINDING,
    ATP_IDX_LWM2M_CON_NOTIFIES,
    ATP_IDX_LWM2M_DTLS_SUFFIX,
    ATP_IDX_ACTIVATE_DTLS_PSK,
    ATP_IDX_MAX = ATP_IDX_ACTIVATE_DTLS_PSK,
    ATP_IDX_SIZE = ATP_IDX_MAX + 1,
};

/**
 * Configuration array cell element
 */
struct AppTxtParser_ConfigLine_S
{
    const char *ATPName; /**< Attribute name at the configuration file */
    const char *DefaultValue; /**< Attribute default value */
    bool Ignore; /**< To specify if the attribute is ignored/deprecated */
    bool Defined; /**< To specify if the attribute has been read from the configuration file */
    char *ATPValue; /**< Attribute value  at the configuration file */
};

typedef struct AppTxtParser_ConfigLine_S AppTxtParser_ConfigLine_T;

/**
 *  The possible token types
 */

enum AppTxtParser_TokensType_E
{
    ATP_TOKEN_TYPE_UNKNOWN,
    ATP_TOKEN_EOF,
    ATP_TOKEN_ATT_NAME,
    ATP_TOKEN_EQUAL,
    ATP_TOKEN_VALUE,
};

typedef enum AppTxtParser_TokensType_E AppTxtParser_TokensType_T;
/**
 *  The states on the configuration file parser states machine
 */
enum AppTxtParser_States_E
{
    ATP_STATE_EXP_NONE,
    ATP_STATE_EXP_NAME,
    ATP_STATE_EXP_EQUAL,
    ATP_STATE_EXP_VALUE,
};

typedef enum AppTxtParser_States_E AppTxtParser_States_T;

static char ATPValues[ATP_IDX_SIZE][APP_TXT_PARSER_LINE_SIZE_MAX];

static AppTxtParser_ConfigLine_T ATPConfigStructure[ATP_IDX_SIZE] =
        {
                { A1Name, ALT_WLAN_SSID_DEFAULT, false, false, ATPValues[0] },
                { A2Name, WLAN_PASSWORD_DEFAULT, false, false, ATPValues[1] },
                { A3Name, WLAN_STATIC_IP_TXT_DEFAULT, false, false, ATPValues[2] },
                { A4Name, WLAN_IP_ADDR_DEFAULT, false, false, ATPValues[3] },
                { A5Name, WLAN_DNS_ADDR_DEFAULT, false, false, ATPValues[4] },
                { A6Name, WLAN_GW_ADDR_DEFAULT, false, false, ATPValues[5] },
                { A7Name, WLAN_MASK_DEFAULT, false, false, ATPValues[6] },
                { A8Name, LWM2M_DEF_SERVER_DEFAULT, false, false, ATPValues[7] },
                { A9Name, LWM2M_ENDPOINT_NAME_DEFAULT, false, false, ATPValues[8] },
                { A10Name, LWM2M_DTLS_PSK_IDENTITY_DEFAULT, false, false, ATPValues[9] },
                { A11Name, LWM2M_DTLS_PSK_SECRET_KEY_DEFAULT, false, false, ATPValues[10] },
                { A12Name, LWM2M_DTLS_PSK_SECRET_KEY_HEX_DEFAULT, false, false, ATPValues[11] },
                { A13Name, LWM2M_TEST_MODE_DEFAULT, false, false, ATPValues[12] },
                { A14Name, LWM2M_LIFETIME_DEFAULT, false, false, ATPValues[13] },
                { A15Name, LWM2M_BINDING_DEFAULT, false, false, ATPValues[14] },
                { A16Name, LWM2M_CON_NOTIFIES_TXT_DEFAULT, false, false, ATPValues[15] },
                { A17Name, APP_TXT_PARSER_EMPTY, true, false, ATPValues[16] }, /* Ignore */
                { A18Name, APP_TXT_PARSER_EMPTY, true, false, ATPValues[17] }, /* Ignore */
        };

/* constant definitions ***************************************************** */

#define ATP_EXPANDED_LENGTH               UINT8_C(65)          /**< length of expanded lwm2m endpoint name and dtls identity */
#define ATP_MAC_LENGTH                    UINT8_C(18)          /**< length of MAC as string */
#define ATP_MAGIC_MAC_EXPAND              "#---MAC----#"       /**< Magic token expanded to MAC */
#define ATP_MAGIC_EP_EXPAND               "#-LWM2M-EP-#"       /**< Magic token expanded to LWM2M endpoint name */

/* local variables ********************************************************** */

static char ATPMacAddress[ATP_MAC_LENGTH] = { 0 };
static char ATPServerURL[ATP_EXPANDED_LENGTH] = { 0 };
static char ATPExpandedEndpointName[ATP_EXPANDED_LENGTH] = { 0 };
static char ATPExpandedPskIdentity[ATP_EXPANDED_LENGTH] = { 0 };
static const char * ATPPskIdentityPtr = NULL;
static const char * ATPPskSecretKeyPtr = NULL;
static uint32_t ATPPskSecretKeyLength = 0UL;
static char ATPKeyBuffer[ATP_EXPANDED_LENGTH] = { 0 };
static const char* ATPHexToBin = "0123456789abcdefABCDEF";
static uint32_t ATPServerPort = 0UL;
static char * ATPServerURLPtr = NULL;

static const char* ATPGetAttValue(int index);

/**
 * @brief Decode hexadecimal string to binary buffer.
 *
 * @param[in] source
 *            Hexadecimal string
 *
 * @param[out] dest
 *             Binary destination buffer.
 *
 * @param[in] terminatedString
 *            Dest should be handled as string (0 terminated)
 *
 * @return number of converted bytes dest (excluding 0 termination, if applied):
 */
static unsigned int ATPDecodeHexToBin(const char* source, char* dest, bool terminatedString);

static void ATPExpandToken(const char* value, const char* magicToken, const char* expand, char* expandedValue, size_t expandedSize);

static bool ATPEmptyCfg(const char *value);

/**
 * @brief extracts tokens from the input buffer , copy into the token buffer and returns its
 * size at tokensize
 *
 * @param[in] buffer
 *            The input buffer
 *
 * @param[in] idxAtBuffer
 *            The index from which we have to look after tokens
 *
 * @param[in] bufSize
 *            The size of the input buffer (param one)
 *
 * @param[out] token
 *            The buffer that will contain the token (must be different to NULL )
 *
 * @param[in] expTokenType
 *            expected token type awaited from attribute parser
 *
 * @param[out] tokenSize
 *            The pointer to the variable that receive the size of the buffer
 *
 * @return the TOKEN types found (ATP_TOKEN_TYPE_UNKNOWN is EOF)
 */
static AppTxtParser_TokensType_T ATPGetToken(const char *buffer, uint16_t *idxAtBuffer,
        uint16_t bufSize, char *token, uint16_t *tokenSize, AppTxtParser_States_T expTokenType)
{
    AppTxtParser_TokensType_T result = ATP_TOKEN_TYPE_UNKNOWN;

    /* erase the OUTPUT token buffer*/
    memset(token, 0, *tokenSize);

    uint8_t index = UINT8_C(0);
    /* don't skip line end if next token is ATP_STATE_EXP_VALUE */
    /* support empty attribute values to deleted compiled defaults */
    const char *skipSet = (expTokenType == ATP_STATE_EXP_VALUE) ? APP_TXT_PARSER_SPACE : APP_TXT_PARSER_WHITESPACE;
    /* Bypass all chars in skip set */
    while ((*idxAtBuffer < bufSize) && (NULL != strchr(skipSet, buffer[*idxAtBuffer])))
    {
        *idxAtBuffer = *idxAtBuffer + 1;
    }

    if (expTokenType == ATP_STATE_EXP_NAME)
    {
        while (buffer[*idxAtBuffer] == '#') // Handling in-line comments for key value pair in the configuration file
        {
            while ((buffer[*idxAtBuffer] != '\n')

            && (buffer[*idxAtBuffer] != '\r')
                    && (*idxAtBuffer < bufSize)) // skip to EOL if since Start of comment tag '#' was found
            {
                *idxAtBuffer = *idxAtBuffer + 1;
            }
            while ((*idxAtBuffer < bufSize) && (NULL != strchr(APP_TXT_PARSER_WHITESPACE, buffer[*idxAtBuffer])))
            {
                *idxAtBuffer = *idxAtBuffer + 1;
            }
        }
    }

    /* is the next char the EQUAL sign */
    if (buffer[*idxAtBuffer] == '=')
    {
        /* YES */
        token[index] = '=';
        *idxAtBuffer = *idxAtBuffer + 1;
        index = index + 1;
        result = ATP_TOKEN_EQUAL;
    }
    else
    {
        /* No , its a string ...*/
        if (expTokenType == ATP_STATE_EXP_VALUE)
        {
            while (
            (buffer[*idxAtBuffer] != '\r') &&
                    (buffer[*idxAtBuffer] != '\n') &&
                    (buffer[*idxAtBuffer] != '\t') &&
                    (*idxAtBuffer < bufSize)
            )
            {
                if (index < *tokenSize - 1)
                {
                    token[index] = buffer[*idxAtBuffer];
                    *idxAtBuffer = *idxAtBuffer + 1;
                    index = index + 1;
                }
            };
        }
        else
        {
            while (
            (buffer[*idxAtBuffer] != '\r') &&
                    (buffer[*idxAtBuffer] != '\n') &&
                    (buffer[*idxAtBuffer] != '\t') &&
                    (buffer[*idxAtBuffer] != ' ') &&
                    (buffer[*idxAtBuffer] != '=') &&
                    (*idxAtBuffer < bufSize)
            )
            {
                if (index < *tokenSize - 1)
                {
                    token[index] = buffer[*idxAtBuffer];
                    *idxAtBuffer = *idxAtBuffer + 1;
                    index = index + 1;
                }
            };
        }
        /* Is this string is a known string ,i.e. attribute name ?*/
        for (uint8_t ConfigIndex = UINT8_C(0); ConfigIndex < (uint8_t) ATP_IDX_SIZE; ConfigIndex++)
        {
            if (0 == strcmp(token, ATPConfigStructure[ConfigIndex].ATPName))
            {
                result = ATP_TOKEN_ATT_NAME;
                break;
            }
        }
        /* The string is not known one, so it's a value */
        if ((index > 0) && (ATP_TOKEN_TYPE_UNKNOWN == result))
        {
            result = ATP_TOKEN_VALUE;
        }
    }
    /* At this point nothing has been found ! */
    if ((index == 0) && (ATP_TOKEN_TYPE_UNKNOWN == result)) /* (index == 0) check was added due to :Warning 438: Last value assigned to variable 'index' (Defined at line 218) not used*/
    {
        result = ATP_TOKEN_EOF;
    }
    return (result);
}

static const char *AppTxtParserLWM2MEndPointValue(void)
{
    const char* value = ATPGetAttValue((int) ATP_IDX_LWM2M_ENDPOINT);
    if (ATPEmptyCfg(value))
    {
        printf("Missing LWM2MENDPOINT !!!\r\n");
    }
    return value;
}

static const char *AppTxtParserDTLSPSKIdentityValue(void)
{
    return ATPGetAttValue((int) ATP_IDX_DTLS_PSK_IDENTITY);
}

static const char *AppTxtParserDTLSPSKSecretKeyValue(void)
{
    return ATPGetAttValue((int) ATP_IDX_DTLS_PSK_SECRET_KEY);
}

static const char *AppTxtParserDTLSPSKSecretKeyHexValue(void)
{
    if (false == ATPConfigStructure[ATP_IDX_DTLS_PSK_SECRET_KEY_HEX].Defined &&
    true == ATPConfigStructure[ATP_IDX_DTLS_PSK_SECRET_KEY].Defined)
    {
        return "";
    }

    return ATPGetAttValue((int) ATP_IDX_DTLS_PSK_SECRET_KEY_HEX);
}

static const char *AppTxtParserLWM2MDefaultServerValue(void)
{
    const char* value = ATPGetAttValue((int) ATP_IDX_LWM2M_DEFAULT_SERVER);
    if (ATPEmptyCfg(value))
    {
        printf("Missing LWM2MDEFSRV !!!\r\n");
    }
    return value;
}

static void AppTxtParserReset(void)
{
    /* Initialize the attribute values holders */
    for (uint8_t i = UINT8_C(0); i < (uint8_t) ATP_IDX_SIZE; i++)
    {
        ATPConfigStructure[i].Defined = false;
        memset(ATPConfigStructure[i].ATPValue, 0U, APP_TXT_PARSER_LINE_SIZE_MAX);
        if (NULL == ATPConfigStructure[i].DefaultValue)
        {
            ATPConfigStructure[i].DefaultValue = APP_TXT_PARSER_EMPTY;
        }
    }
}

/**
 * @brief Parse the config file read inside the buffer
 *
 * @param[in] initCredentials
 * credentials for parsing
 *
 * @return true if configuration file is correct and contains necessary attribute/values
 *
 */
static bool AppTxtParserInitialize(AppTxtParser_Init_T * initCredentials)
{
    bool result = true;
    uint16_t atpIdxBuffer = 0U;
    AppTxtParser_States_T atpState = ATP_STATE_EXP_NAME;
    int8_t atpCurrentConfigToSet = INT8_C(0);
    char atpToken[APP_TXT_PARSER_LINE_SIZE_MAX] = { 0 };
    uint16_t atpOutputTokenSize = 0U;
    AppTxtParser_TokensType_T atpTokenType = ATP_TOKEN_TYPE_UNKNOWN;
    const char * defaultServerUrl = NULL;
    uint8_t urlStartIndex = 0U;
    char * portPtr;

    /* Initialize the attribute values holders */
    AppTxtParserReset();

    while (true == result)
    {
        atpOutputTokenSize = APP_TXT_PARSER_LINE_SIZE_MAX;
        atpTokenType = ATPGetToken((const char *) initCredentials->DataPointer,
                &atpIdxBuffer, initCredentials->DataLength, atpToken, &atpOutputTokenSize, atpState);
        if (atpTokenType == ATP_TOKEN_EOF)
        {
            break;
        }

        switch (atpState)
        {
        case ATP_STATE_EXP_NAME:
            if (atpTokenType != ATP_TOKEN_ATT_NAME)
            {
                printf("Expecting NAME at %u\r\n", (uint16_t) (atpIdxBuffer - strlen(atpToken)));
                result = false;
                break;
            }
            for (uint8_t i = 0; i < (uint8_t) ATP_IDX_SIZE; i++)
            {
                if (strcmp(atpToken, ATPConfigStructure[i].ATPName) == UINT8_C(0))
                {
                    atpCurrentConfigToSet = i;
                    atpState = ATP_STATE_EXP_EQUAL;
                    break;
                }
            }
            break;

        case ATP_STATE_EXP_EQUAL:
            if (atpTokenType != ATP_TOKEN_EQUAL)
            {
                printf("Expecting sign '=' at %u\r\n", (uint16_t) (atpIdxBuffer - strlen(atpToken)));
                result = false;
                break;
            }
            atpState = ATP_STATE_EXP_VALUE;
            break;

        case ATP_STATE_EXP_VALUE:
            if (atpTokenType != ATP_TOKEN_VALUE)
            {
                printf("Expecting value string at %d\r\n", (uint16_t) (atpIdxBuffer - strlen(atpToken)));
                result = false;
                break;
            }
            strcpy(ATPConfigStructure[atpCurrentConfigToSet].ATPValue, atpToken);

            if (ATPConfigStructure[atpCurrentConfigToSet].Defined == 0)
            {
                ATPConfigStructure[atpCurrentConfigToSet].Defined = 1;
                atpState = ATP_STATE_EXP_NAME;
            }
            else
            {
                result = false;
                printf("Twice definition of  attribute %s! \r\n", ATPConfigStructure[atpCurrentConfigToSet].ATPName);
            }
            break;

        default:
            printf("Unexpected state %d \r\n", atpState);
            break;
        }
    }

    /* Special defaults */
    if (true == ATPConfigStructure[ATP_IDX_DTLS_PSK_SECRET_KEY].Defined)
    {
        /**
         * If the secret key is provided in the config.txt, disable the
         * compiled default for secret key hex to ensure, that the provided
         * key is used and not the default hexadecimal key.
         */
        ATPConfigStructure[ATP_IDX_DTLS_PSK_SECRET_KEY_HEX].DefaultValue = APP_TXT_PARSER_EMPTY;
    }
    if (true == ATPConfigStructure[ATP_IDX_LWM2M_ENDPOINT].Defined)
    {
        /**
         * If the LWM2M endpoint name is provided in the config.txt, disable the
         * compiled default for DTLS PSK identity to ensure, that the provided
         * LWM2M endpoint name is used and not the default DTLS PSK identity.
         */
        ATPConfigStructure[ATP_IDX_DTLS_PSK_IDENTITY].DefaultValue = APP_TXT_PARSER_EMPTY;
    }

    snprintf(ATPMacAddress, sizeof(ATPMacAddress), "%02X-%02X-%02X-%02X-%02X-%02X",
            initCredentials->DeviceMAC[0], initCredentials->DeviceMAC[1],
            initCredentials->DeviceMAC[2], initCredentials->DeviceMAC[3],
            initCredentials->DeviceMAC[4], initCredentials->DeviceMAC[5]);

    snprintf(ATPKeyBuffer, sizeof(ATPKeyBuffer), "%02X%02X%02X%02X%02X%02X",
            initCredentials->DeviceMAC[0], initCredentials->DeviceMAC[1],
            initCredentials->DeviceMAC[2], initCredentials->DeviceMAC[3],
            initCredentials->DeviceMAC[4], initCredentials->DeviceMAC[5]);
    ATPExpandToken(AppTxtParserLWM2MEndPointValue(), ATP_MAGIC_MAC_EXPAND, ATPKeyBuffer, ATPExpandedEndpointName, sizeof(ATPExpandedEndpointName));

    if (ATPEmptyCfg(AppTxtParserDTLSPSKIdentityValue()))
    {
        ATPPskIdentityPtr = ATPExpandedEndpointName;
    }
    else
    {
        /* expand MAC */
        ATPExpandToken(AppTxtParserDTLSPSKIdentityValue(), ATP_MAGIC_MAC_EXPAND, ATPKeyBuffer, ATPExpandedPskIdentity, sizeof(ATPExpandedPskIdentity));
        /* expand LWM2M EP */
        ATPExpandToken(ATPExpandedPskIdentity, ATP_MAGIC_EP_EXPAND, ATPExpandedEndpointName, ATPKeyBuffer, sizeof(ATPKeyBuffer));
        /* copy expanded identity to destination */
        strncpy(ATPExpandedPskIdentity, ATPKeyBuffer, sizeof(ATPExpandedPskIdentity) - 1);
        ATPPskIdentityPtr = ATPExpandedPskIdentity;
    }

    if (ATPEmptyCfg(AppTxtParserDTLSPSKSecretKeyHexValue()))
    {
        if (ATPEmptyCfg(AppTxtParserDTLSPSKSecretKeyValue()))
        {
            printf("DTLS/PSK please Provide the secret key!\r\n");
        }
        else
        {
            ATPPskSecretKeyPtr = AppTxtParserDTLSPSKSecretKeyValue();
            ATPPskSecretKeyLength = strlen(ATPPskSecretKeyPtr);
        }
    }
    else
    {
        memset(ATPKeyBuffer, 0, sizeof(ATPKeyBuffer));
        ATPPskSecretKeyLength = ATPDecodeHexToBin(AppTxtParserDTLSPSKSecretKeyHexValue(), ATPKeyBuffer, false);
        ATPPskSecretKeyPtr = ATPKeyBuffer;
    }

    defaultServerUrl = AppTxtParserLWM2MDefaultServerValue();

    if (!ATPEmptyCfg(defaultServerUrl))
    {
        memcpy(ATPServerURL, defaultServerUrl, strlen(defaultServerUrl));
        if (strlen(ATPServerURL) >= 8U)
        {
            if (AppTxtParser_LWM2MIsSecureValue())
            {
                urlStartIndex = 8U;
            }
            else
            {
                urlStartIndex = 7U;
            }
            portPtr = strstr(ATPServerURL + urlStartIndex, ":");
            if (NULL != portPtr)
            {
                *portPtr = '\0';
                portPtr += 1;
            }
            if (NULL != portPtr)
            {
                ATPServerPort = (uint32_t) atol(portPtr);
                ATPServerURLPtr = &ATPServerURL[urlStartIndex];
            }
        }
        else
        {
            printf("LWM2MDEFSRV is not proper. Provide something like coap://<serverUrl>:<serverPort> !!!\r\n");
        }
    }

    return result;
}

/** Refer interface header for description */
void AppTxtParser_PrintConfigurations(const char* title, bool defaultsOnly)
{
    printf("%s\n\r", title);
    for (uint8_t i = UINT8_C(0); i < (uint8_t) ATP_IDX_SIZE; i++)
    {
        if ((uint8_t) ATP_IDX_WLAN_PASSWORD == i)
        {
            continue;
        }
        else
        {
            if (false == ATPConfigStructure[i].Ignore)
            {
                if (true == ATPConfigStructure[i].Defined)
                {
                    printf("[%19s] = [%s]\r\n", ATPConfigStructure[i].ATPName, ATPConfigStructure[i].ATPValue);
                }
                else
                {
                    if (true == defaultsOnly)
                    {
                        if (0 != *ATPConfigStructure[i].DefaultValue)
                        {
                            printf("[%19s] * [%s]\r\n", ATPConfigStructure[i].ATPName, ATPConfigStructure[i].DefaultValue);
                        }
                    }
                    else
                    {
                        printf("[%19s] * [%s] (DEFAULT)\r\n", ATPConfigStructure[i].ATPName, ATPConfigStructure[i].DefaultValue);
                    }
                }
            }
            else if (true == ATPConfigStructure[i].Defined)
            {
                printf("[%19s] is deprecated and should be removed\r\n", ATPConfigStructure[i].ATPName);
            }
        }
    }
}

static const char* ATPGetAttValue(int index)
{
    if (0 <= index && index < (int) ATP_IDX_SIZE)
    {
        if (true == ATPConfigStructure[index].Defined)
        {
            return ATPConfigStructure[index].ATPValue;
        }
        else
        {
            return ATPConfigStructure[index].DefaultValue;
        }
    }
    return "";
}

/**
 * @brief Convert hexadecimal character/digit to number.
 *
 * '0' ... '9' to 0 ... 9
 * 'a' ... 'f' to 10 ... 15
 * 'A' ... 'F' to 10 ... 15
 *
 * @param[in] c
 *            character to be converted
 *
 * @return number, or -1, if provided character wasN#t a valid hexadecimal character.
 */
static int digitHexToBin(char c)
{
    int r = 0;
    char* p = strchr(ATPHexToBin, c);
    if (NULL == p)
    {
        printf("Character '%c' is no hex-digit!\r\n", c);
        assert(false);
        return -1;
    }
    r = p - ATPHexToBin;
    if (15 < r)
    {
        /* upper case character */
        r -= 6;
    }
    return (r & 0xf);
}

static unsigned int ATPDecodeHexToBin(const char* source, char* dest, bool terminatedString)
{
    unsigned int srcIndex = 0;
    unsigned int dstIndex = 0;
    unsigned int len = strlen(source);
    if (len & 1)
    {
        printf("Hex %s has odd length!\r\n", source);
        assert(false);
    }
    while (srcIndex < len)
    {
        char b = 0;
        int d = digitHexToBin(source[srcIndex++]);
        if (0 <= d)
        {
            b = (d & 0xf) << 4;
            d = digitHexToBin(source[srcIndex++]);
            if (0 <= d)
            {
                b |= (d & 0xf);
            }
            else
            {
                break;
            }
        }
        else
        {
            break;
        }
        if (terminatedString && 0 == b)
        {
            printf("Hex 00 is not supported!\r\n");
            break;
        }
        dest[dstIndex++] = b;
    }
    if (terminatedString)
    {
        dest[dstIndex] = 0;
    }
    return dstIndex;
}

static void ATPExpandToken(const char* value, const char* magicToken, const char* expand, char* expandedValue, size_t expandedSize)
{
    const char* magic = strstr(value, magicToken);
    size_t length = 0;
    size_t left = expandedSize - 1;

    memset(expandedValue, 0, expandedSize);

    if (NULL == magic)
    {
        /* no magic token expand, just copy */
        length = strlen(value);
        if (left < length)
        {
            printf("value too long! max %u, length %u\n", left, length);
        }
        strncpy(expandedValue, value, left);
    }
    else
    {
        size_t length = magic - value;

        if (0 < length)
        {
            /* copy head */
            if (left < length)
            {
                printf("header too long! max %u, length %u\n", left, length);
                length = left;
            }
            memcpy(expandedValue, value, length);
            expandedValue += length;
            left -= length;
        }
        /* expand token */
        length = strlen(expand);
        if (left < length)
        {
            printf("expand too long! max left %u, length %u\n", left, length);
            length = left;
        }
        memcpy(expandedValue, expand, length);
        expandedValue += length;
        left -= length;

        /* move magic to tail after MAGIC token in the source */
        magic += strlen(magicToken);
        if (0 != *magic)
        {
            /* copy tail */
            length = strlen(magic);
            if (left < length)
            {
                printf("tail too long! max left %u, length %u\n", left, length);
                length = left;
            }
            memcpy(expandedValue, magic, length);
        }
    }
}

static bool ATPEmptyCfg(const char *value)
{
    return (NULL == value) || (0 == *value);
}

/* global variables ********************************************************* */
/** Refer interface header for description */

#define XDK_APP_MODULE_ID_APP_TXT_PARSER   1791


Retcode_T AppTxtParser_Initialize(AppTxtParser_Init_T * initCredentials)
{
    if (true == AppTxtParserInitialize(initCredentials))
    {
        AppTxtParser_PrintConfigurations("Application Configuration:", false);
    }
    else
    {
        return RETCODE(RETCODE_SEVERITY_ERROR, (uint32_t ) RETCODE_FAILURE);
    }
    return RETCODE_OK;

}

/** Refer interface header for description */
const char *AppTxtParser_WLANSsidValue(void)
{
    const char * wlanSsidValue = ATPGetAttValue((int) ATP_IDX_WLAN_SSID);
    if (ATPEmptyCfg(wlanSsidValue))
    {
        printf("Missing SSID !!!\r\n");
    }
    return wlanSsidValue;
}

/** Refer interface header for description */
const char *AppTxtParser_WLANPasswordValue(void)
{
    const char * wlanPasswordValue = ATPGetAttValue((int) ATP_IDX_WLAN_PASSWORD);
    if (ATPEmptyCfg(wlanPasswordValue))
    {
        printf("Missing PASSWORD !!!\r\n");
    }
    return wlanPasswordValue;
}

/** Refer interface header for description */
bool AppTxtParser_WLANStaticIpValue(void)
{
    bool RetVal = false;
    const char* value = ATPGetAttValue((int) ATP_IDX_WLAN_STATIC_IP);
    if (ATPEmptyCfg(value))
    {
        printf("Missing STATIC !!!\r\n");
    }
    else
    {
        if (0 == strcmp(APP_TXT_PARSER_YES, value))
        {
            RetVal = true;
        }
        else if (0 == strcmp(APP_TXT_PARSER_NO, value))
        {
            RetVal = false;
        }
        else
        {
            printf("STATIC_IP was not defined properly in file system. It is disabled. \r\n");
        }
    }
    return (RetVal);
}

/** Refer interface header for description */
uint32_t AppTxtParser_WLANIpAddrValue(void)
{
    uint32_t ipAddr = 0UL;
    Retcode_T retcode = RETCODE_OK;
    const char* value = ATPGetAttValue((int) ATP_IDX_WLAN_IP_ADDR);
    if (ATPEmptyCfg(value))
    {
        printf("Missing STATICIP !!!\r\n");
    }
    else
    {
        retcode = Utils_ConvertIpStringToNumber(value, &ipAddr);
    }
    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
    }
    return ipAddr;
}

/** Refer interface header for description */
uint32_t AppTxtParser_WLANDnsAddrValue(void)
{
    uint32_t dnsAddr = 0UL;
    Retcode_T retcode = RETCODE_OK;

    const char* value = ATPGetAttValue((int) ATP_IDX_WLAN_DNS_ADDR);
    if (ATPEmptyCfg(value))
    {
        printf("Missing DNSSERVER !!!\r\n");
    }
    else
    {
        retcode = Utils_ConvertIpStringToNumber(value, &dnsAddr);
    }
    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
    }
    return dnsAddr;
}

/** Refer interface header for description */
uint32_t AppTxtParser_WLANGwAddrValue(void)
{
    uint32_t gwAddr = 0UL;
    Retcode_T retcode = RETCODE_OK;

    const char* value = ATPGetAttValue((int) ATP_IDX_WLAN_GW_ADDR);
    if (ATPEmptyCfg(value))
    {
        printf("Missing GATEWAY !!!\r\n");
    }
    else
    {
        retcode = Utils_ConvertIpStringToNumber(value, &gwAddr);
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
    }

    return gwAddr;
}

/** Refer interface header for description */
uint32_t AppTxtParser_WLANMaskValue(void)
{
    uint32_t mask = 0UL;
    Retcode_T retcode = RETCODE_OK;

    const char* value = ATPGetAttValue((int) ATP_IDX_WLAN_MASK);
    if (ATPEmptyCfg(value))
    {
        printf("Missing MASK !!!\r\n");
    }
    else
    {
        retcode = Utils_ConvertIpStringToNumber(value, &mask);
    }
    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
    }

    return mask;
}

/** Refer interface header for description */
bool AppTxtParser_LWM2MIsSecureValue(void)
{
    bool isSecure = false;

    if (strncmp("coaps://", ATPServerURL, 8) == 0)
    {
        isSecure = true;
    }
    else if (strncmp("coap://", ATPServerURL, 7) == 0)
    {
        isSecure = false;
    }
    else
    {
        printf("Only coap:// (plain) or coaps:// (encrypted) are supported!\r\n");
        printf("Invalid LWM2MDEFSRV string. Assuming non-secure !!!\r\n");
    }
    return (isSecure);
}

/** Refer interface header for description */
const char *AppTxtParser_LWM2MMACAddrValue(void)
{
    return ATPMacAddress;
}

/** Refer interface header for description */
const char *AppTxtParser_LWM2MServerURLValue(void)
{
    return ATPServerURLPtr;
}

/** Refer interface header for description */
uint32_t AppTxtParser_LWM2MServerPortValue(void)
{
    return ATPServerPort;
}

/** Refer interface header for description */
const char *AppTxtParser_LWM2MEndPointValue(void)
{
    return ATPExpandedEndpointName;
}

/** Refer interface header for description */
const char *AppTxtParser_DTLSPSKIdentityValue(void)
{
    return ATPPskIdentityPtr;
}

/** Refer interface header for description */
const char *AppTxtParser_DTLSPSKSecretKeyValue(void)
{
    return ATPPskSecretKeyPtr;
}

/** Refer interface header for description */
uint32_t AppTxtParser_DTLSPSKSecretKeyLengthValue(void)
{
    return ATPPskSecretKeyLength;
}

/** Refer interface header for description */
uint8_t AppTxtParser_LWM2MTestModeValue(void)
{
    uint8_t result = LWM2M_TESTMODE_MIX;
    const char* Value = ATPGetAttValue((int) ATP_IDX_TEST_MODE);
    if (0 == strcmp(LWM2M_TEST_MODE_DEFAULT, Value))
    {
        result = LWM2M_TESTMODE_MIX;
    }
    else if (0 == strcmp(APP_TXT_PARSER_YES, Value))
    {
        result = LWM2M_TESTMODE_ON;
    }
    else if (0 == strcmp(APP_TXT_PARSER_NO, Value))
    {
        result = LWM2M_TESTMODE_OFF;
    }
    return result;
}

/** Refer interface header for description */
uint32_t AppTxtParser_LWM2MLifetimeValue(void)
{
    return (uint32_t) atol(ATPGetAttValue((int) ATP_IDX_LWM2M_LIFETIME));
}

/** Refer interface header for description */
const char *AppTxtParser_LWM2MBindingValue(void)
{
    return ATPGetAttValue((int) ATP_IDX_LWM2M_BINDING);
}

/** Refer interface header for description */
bool AppTxtParser_LWM2MConNotifiesValue(void)
{
    bool result = false;
    const char* Value = ATPGetAttValue((int) ATP_IDX_LWM2M_CON_NOTIFIES);
    if (0 == strcmp(APP_TXT_PARSER_YES, Value))
    {
        result = true;
    }
    return result;
}


static void strrspn(char * ps, char *pe ){
    if( ps == NULL ) return;
    if( pe == NULL )
        pe = ps + strlen(ps)-1;
    while( pe >= ps && (*pe==' '||*pe=='\t')){
        *pe-- = 0;
    }
}

static int cfgItem(char * pit, cbCfgProcKeyVal pfnxParseKeyVal){
    int ret =0 ;
    pit += strspn(pit, " \t");
    char * pval = strchr(pit, '=');
    if( pval ) {
        strrspn(pit, pval-1);
        *pval++ = 0;
        pval += strspn(pval, " \t=");
        strrspn(pval, NULL);
        if( pfnxParseKeyVal ){
            if( (ret=pfnxParseKeyVal(pit,pval)) < 0 ) return ret;
        }
    }
    return ret;
}

int parseCfgCotent(char * ramBufferRead, cbCfgProcKeyVal pfnxParseKeyVal ){
    int ret = 0;
    char *p = strtok(ramBufferRead, "\r\n;");
    while (p) {
        if( (ret=cfgItem(p, pfnxParseKeyVal)) < 0 ) return ret;
        p = strtok(NULL, "\r\n");
    }
    return 0;
}
